/**
 * @description: 线程常用的一些方法
 * currentThread()：取得当前正在运行的线程对象
 * <p>
 * .currentThread().getName()：获得当前线程名称
 * <p>
 * .currentThread().getName()：设置当前线程名称
 * <p>
 *  线程的名称一般在启动线程前设置： newThread(mt,"线程-A").start() ;
 * <p>
 *  如果没有手动设置，则系统自动设置线程名称。
 * <p>
 * .isAlive()：判断线程是否启动
 * <p>
 * .join()：暂停其他线程，强制优先执行该线程
 * <p>
 * .sleep(时间)：设置线程休眠一段时间，到时间后自动启动
 * <p>
 * .interrupt()：中断线程执行
 * <p>
 * .setDaemon(true)：设置线程在后台执行
 * <p>
 * 线程优先级分类：最低，最高，普通
 * （Thread.MIN_PRIORITY，Thread.MAX_PRIORITY，Thread.NORM_PRIORITY）
 * .setPriority（）：设置优先级
 * .getPriority（）：获取优先级
 * @author: zys
 * @create: 2020-10-19 11:29
 **/
public class MyThreadMethod {

    public static void main(String[] args) {
     //   testSleep();
      // testYield();
        //testJoin();
       //testWait();
        //  testWaitTimeout();
    }

    /**
     * 创建线程实例
     */
    static class ThreadB extends Thread {
        public ThreadB(String name) {
            super(name);
        }

        public void run() {
            System.out.println(Thread.currentThread().getName() + " run ");
            // 死循环，不断运行。
            while (true) {
            }  //  这个线程与主线程无关，无 synchronized
        }
    }

    /**
     * Wait()有参数测试 主线程等待wait超时才进行释放
     * 使用notify() 或者notifyAll()
     * 等待wait()超时
     * 调用stop() (过期了的，不建议使用的，因为stop()在结束一个线程时并不会保证线程的资源正常释放，会导致程序可能会出现一些不确定的状态)
     * 调用interrupt() (interrupt方法和start方法一样，都是native方法,当其他线程调用当前线程的interrupt方法时，即设置了一个标识，
     * 表示当前线程可以中断了，至于什么时候中断，取决于当前线程。当前线程通过调用isInterrupted（）方法来判断自身是否被中断，
     * 如果返回true,则说明当前线程已经中断，这种通过标识位或中断操作的方式能够使线程在终止时有机会清理资源，而这种方式也更加安全。)
     */
    private static void testWaitTimeout() {
        ThreadB t1 = new ThreadB("t1");
        synchronized (t1) {
            try {
                // 启动“线程t1”
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();
                // 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒，或超过3000ms延时；然后才被唤醒。
                System.out.println(Thread.currentThread().getName() + " call wait ");
                t1.wait(3000);
                System.out.println(Thread.currentThread().getName() + " continue");
                t1.stop();
                //t1.interrupt();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建线程实例
     */
    static class ThreadA extends Thread {
        public ThreadA(String name) {
            super(name);
        }

        public void run() {
            synchronized (this) {
                try {
                    Thread.sleep(1000); //  使当前线阻塞 1 s，确保主程序的 t1.wait(); 执行之后再执行 notify()
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " call notify()");
                // 唤醒当前的wait线程
                this.notify();
                // 唤醒所有的wait线程
                //this.notifyAll();
            }
        }
    }

    /**
     * Wait()无参数测试
     * 当线程执行wait()时，会把当前的锁释放，然后让出CPU，进入等待状态。
     * 主线程让行等待notify() 或者notifyAll唤醒()
     */
    static void testWait() {
        ThreadA t1 = new ThreadA("t1");
        synchronized (t1) {
            try {
                // 启动“线程t1”
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();
                // 主线程等待t1通过notify()唤醒。
                System.out.println(Thread.currentThread().getName() + " wait()");
                // 中断 调用wait()方法进入等待队列的 线程
                t1.wait();
                System.out.println(Thread.currentThread().getName() + " continue");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * sleep()方法是Thread类的方法，因此它不能改变对象的锁。所以当在一个Synchronized方法中调用sleep（）时，线程虽然休眠了，
     * 但是对象的机锁没有被释放，其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁，其他线程可以访问该对象。
     */
    static void testSleep() {
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("A" + i);
                try {
                    Thread.sleep(50000);
                    //TimeUnit.MICROSECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * Yield，当前线程正在执行的时候停下来进入等等待队列，回到等待队列里，在系统调度的算法依然是有可能拿出来继续被执行，
     * 更大可能是把原来等待中的任务拿出来一个进行执行，通俗来讲就是说调用yield()方法暂时的让出一下cpu,后面能不能抢到的话需要看cpu的调度
     */
    static void testYield() {
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("A" + i);
                if (i % 10 == 0) {
                    Thread.yield();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println("B" + i);
                if (i % 10 == 0) {
                    Thread.yield();
                }
            }
        }).start();
    }

    /**
     * join,在自己当前线程加入需要调用join的线程，则本线程等待。等待调用的线程执行完毕，自己再去执行，
     * t1和t2两个线程，在t2的某个节点调用了t1.join(),他会跑到t1去执行，等待t1调用完毕继续运行t2
     */
    static void testJoin() {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("t1" + i);
                try {
                    Thread.sleep(500);
                    //TimeUnit.MICROSECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("t2" + i);
                try {
                    Thread.sleep(500);
                    //TimeUnit.MICROSECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
    }

}
